package com.tansun.tandata.utils;


import org.springframework.util.StringUtils;

import javax.persistence.Column;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;

public class ClassUtils {
    /**
     * 通过字段名从对象或对象的父类中得到静态字段的值
     * @param clazz 对象class
     * @param fieldName 字段名
     * @return 字段对应的值
     * @throws Exception
     */
    public static Object getValueOfStatic(Class<?> clazz, String fieldName) throws Exception {
        if (clazz == null) {
            return null;
        }
        if (StringUtils.isEmpty(fieldName)) {
            return null;
        }
        Field field = null;
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(clazz);
            } catch (Exception e) {
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }
        return null;
    }
    /**
     * 通过字段名从对象或对象的父类中得到字段的值
     * @param object 对象实例
     * @param fieldName 字段名
     * @return 字段对应的值
     * @throws Exception
     */
    public static Object getValue(Object object, String fieldName) throws Exception {
        if (object == null) {
            return null;
        }
        if (StringUtils.isEmpty(fieldName)) {
            return null;
        }
        Field field = null;
        Class<?> clazz = object.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field.get(object);
            } catch (Exception e) {
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }
        return null;
    }


    /**
     * 通过字段名从对象或对象的父类中得到字段的值（调用字典的get方法）
     * @param object 对象实例
     * @param fieldName 字段名
     * @return 字段对应的值
     * @throws Exception
     */
    public static Object getValueOfGet(Object object, String fieldName) throws Exception {
        if (object == null) {
            return null;
        }
        if (StringUtils.isEmpty(fieldName)) {
            return null;
        }
        Field field = null;
        Class<?> clazz = object.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                //获得get方法
                Method getMethod = pd.getReadMethod();
                //执行get方法返回一个Object
                return getMethod.invoke(object);
            } catch (Exception e) {
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }

        return null;
    }

    /**
     * 通过字段名从对象或对象的父类中得到字段的值（调用字典的get方法，可以取出复杂的对象的值）
     * @param object 对象实例
     * @param fieldName 字段名
     * @return 字段对应的值
     * @throws Exception
     */
    public static Object getValueOfGetIncludeObjectFeild(Object object, String fieldName)
            throws Exception {


        if (object == null) {
            return null;
        }
        if (StringUtils.isEmpty(fieldName)) {
            return null;
        }

        if(HashMap.class.equals(object.getClass())){
            return ((Map)object).get(fieldName);
        }

        Field field = null;
        Class<?> clazz = object.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            try {
                if (fieldName.contains(".")) {
                    // 如：operatorUser.name、operatorUser.org.name，递归调用
                    String[] splitFiledName = fieldName.split("\\.");
                    return getValueOfGetIncludeObjectFeild(
                            getValueOfGetIncludeObjectFeild(object, splitFiledName[0]),
                            splitFiledName[1]);
                }
                field = clazz.getDeclaredField(fieldName);
                field.setAccessible(true);

                PropertyDescriptor pd = new PropertyDescriptor(field.getName(), clazz);
                //获得get方法
                Method getMethod = pd.getReadMethod();
                //执行get方法返回一个Object
                return getMethod.invoke(object);
            } catch (Exception e) {
                //这里甚么都不要做！并且这里的异常必须这样写，不能抛出去。
                //如果这里的异常打印或者往外抛，则就不会执行clazz = clazz.getSuperclass(),最后就不会进入到父类中了
            }
        }

        return null;
    }

    /**
     * 执行class中的方法
     * @param clazz 目标class
     * @param methodName 方法名
     * @param args 参数
     * @return
     * @throws Exception
     */
    public static Object runFunctionByClass(Class<?> clazz,String methodName,Object... args)throws Exception {
        if(clazz==null || StringUtils.isEmpty(methodName)){
            return null;
        }
        Method method = clazz.getMethod(methodName);
        Object obj = clazz.newInstance();
        Object result = method.invoke(obj,args);
        return result;
    }

    /**
     * 执行实体类中的方法
     * @param obj 目标实体类
     * @param methodName 方法名
     * @param args 参数
     * @return
     * @throws Exception
     */
    public static Object runFunction(Object obj,String methodName,Object... args)throws Exception {
        if(obj==null || StringUtils.isEmpty(methodName)){
            return null;
        }
        Method method = obj.getClass().getMethod(methodName);
        Object result = method.invoke(obj,args);
        return result;
    }

    /**
     * 获取实体类中有@Column注解的字段
     * @param obj 目标实体类
     * @param containNull 是否包含值为null的字段
     * @return
     * @throws Exception
     */
    public static List<String> getColumnFields(Object obj,boolean containNull)throws Exception {
        List<String> list = new ArrayList<>();
        Field field;
        String fieldName;
        Class<?> clazz = obj.getClass();
        for (; clazz != Object.class; clazz = clazz.getSuperclass()) {
            Field[] fields = clazz.getDeclaredFields();
            for (int i = 0; i <fields.length ; i++) {
                fields[i].setAccessible(true);
                field = obj.getClass().getDeclaredField(fields[i].getName());
                Column column = field.getAnnotation(Column.class);
                if(column != null){
                    fieldName = fields[i].getName();
                    if(!containNull){
                        if( fields[i].get(obj)==null){
                            continue;
                        }
                    }
                    list.add(fieldName);
                }
            }
        }
        return list;
    }




}
